package graphgenetics;

/*
 * TODO:
 * Start over if the best has not improved in n trials?
 * Make it so that the first 21 bits of the bit string are the same every time.
 * Flip 0 or more bits with each merge of two bitstrings.
 * Add different kinds of mutation.
 *
 * Watch somewhere for a graph with no fives in the trees, and print
 * off the graph (or save to a file).
 * Thead populations.
 * Improve ranking.
 * A way to mutate an existing graph.
 * A way to "dump" the population to a file for use later
 * (and a way to read in a population from a file).
 * Add input parameter to modify the crossing probability (of switching
 * to the other graph). Number can change?
 * Have a progress meter (maybe write a line every 5 or 10 %
 * of the way done) so that you can tell if a run is going to take too long.
 * A way to print statistics about the population (e.g. max, min, and median
 * ranks, etc)
 *
 * Maybe:
 * (When combining) Flip one bitstring if it
 * has more differerces than similarities?
 * Mutate existing elements to create new ones insead of merging two?
 * Cross-over technique stored in the population members?
 */

import java.util.Random;

public class Main {

    private Random r;
    private int length, popSize, iterations;
    private double maxRank;
    private boolean showNewBest;
    private TreeGenerator tg;
    private RandomArrayBuilder rab;
    private Population<GraphItem> population;

    /**
     * Runs the program.
     * The parameters that can be used are:
     * <ul>
     * <li> i=nnn where nnn is a number: the number of iterations </li>
     * <li> p=nnn where nnn is a number: the size of the population </li>
     * <li> maxrank=nnn where nnn is a number: the max rank that something
     * can have ans still be allowed in to the population. </li>
     * <li> show=t or show=f (t is true and f is false): switches whether
     * it shows the current best graph each time that changes. Useful
     * for long runs.</li>
     * </ul>
     * @param args The command line parameters
     */
    public static void main(String[] args) {
        Main runner = new Main(args);
        runner.run();
    }

    /**
     * Creates the objects and sets up the variables
     */
    private Main(String[] args) {
        // Default settings
        length = 43;
        popSize = 1000;
        iterations = 1000;
        maxRank = 1200.0;
        showNewBest = false;

        // Deal with settings
        parseArgs(args);
        printSettings();

        // Create the objects
        tg = new TreeGenerator(length);
        population= new Population<GraphItem>(popSize, maxRank);
        r = getRandom();
        rab = new RandomArrayBuilder(r, length);
    }

    /**
     * Reads throught the command line arguments to get settings out of
     * them.
     * @param args The command line arguments
     */
    private void parseArgs(String[] args) {
        for (String a : args) {
            if ( a.startsWith("i=")) {
                try {
                    iterations = Integer.parseInt(a.substring(2));
                } catch (NumberFormatException e) {
                    System.err.println("Error with: " + a);
                }
            } else if (a.startsWith("p=")) {
                try {
                    popSize = Integer.parseInt(a.substring(2));
                } catch (NumberFormatException e) {
                    System.err.println("Error with: " + a);
                }
            } else if (a.startsWith("maxrank=")) {
                try {
                    popSize = Integer.parseInt(a.substring(8));
                } catch (NumberFormatException e) {
                    System.err.println("Error with: " + a);
                }
            } else if (a.startsWith("show=")) {
                if (a.equals("show=t")) {
                    showNewBest = true;
                } else if (a.endsWith("show=f")) {
                    showNewBest = false;
                } else {
                    System.err.println("Error with: " + a);
                }
            } else {
                System.err.println("Error with: " + a);
            }
        }
    }

    /**
     * Prints the current settings.
     */
    private void printSettings() {
        System.out.println("Settings are: ");
        System.out.println("Iterations: " + iterations);
        System.out.println("size (of the graphs): " + length);
        System.out.println("Max rank: " + maxRank);
        System.out.println("Population size: " + popSize);
    }

    /**
     * Runs the program, and prints off the results
     */
    public void run() {
        fillPop(population, popSize);
        System.out.println("Filled population");
        iterate(iterations, population);
        printBest(population);
        System.out.println(population.getFirst().countCliques());
    }

    /**
     * Make a random object using a ramdom seed, but prints off the seed.
     * @return A random object
     */
    private static Random getRandom() {
        Random x = new Random();
        long seed = x.nextLong();
        System.out.println("Using seed: " + seed);
        return new Random(seed);
    }

    /**
     * Adds a single population member
     * @param p
     */
    private void addPopulationMember(Population<GraphItem> p) {
        GraphItem g = new GraphItem(r, length, true, tg, rab);
        p.insert(g);
    }

    /**
     * This inserts the graph from the article used in
     * making the K42 with no 5-cliques, and other graphs
     * made from it.
     * @param p The population to insert the graph into.
     */
    public void insertSpecial(Population<GraphItem> p) {
        // 43 5-cliques
        GraphItem g = new GraphItem("110000100101110101011110101011101001000011110000100101110101011110101011101001000011100001001011101010111101010111010010000110000100101110101011110101011101001000110000100101110101011110101011101001001100001001011101010111101010111010010110000100101110101011110101011101001110000100101110101011110101011101001100001001011101010111101010111010110000100101110101011110101011101110000100101110101011110101011101100001001011101010111101010111110000100101110101011110101011110000100101110101011110101011100001001011101010111101010110000100101110101011110101110000100101110101011110101100001001011101010111101110000100101110101011110110000100101110101011111100001001011101010111110000100101110101011110000100101110101011100001001011101010110000100101110101110000100101110101100001001011101110000100101110110000100101111100001001011110000100101110000100101100001001110000100110000101100001110000110001100110111",
                length, tg, rab);
        //p.insert(g);
        // 37 5-cliques
        g = new GraphItem("110000100101110101011110101011101001000011110000100101110101011110101011101001000011100001001011101010111101010111010010000110000100101110101011110101011101001000110000100101110101011110101011101001001100001001011101010111101010111010010010000100101110101011110101011101001110000100101110101011110101011101001100001001011101010111101010111010110000100101110101011110101011101110000100101110101011110101011101100001001011101010111101010111110000100101110101011110101011110000100101110101011110101011100001001011101010111101010110000100101110101011110101110000100101110101011110101100001001011101010111101110000100101110101011110110000100101110101011111100001001011101010111110000100101110101011010000100101110101011100001001011101010110000100101110101110000100101110101100001001011101110000100101110110000100101111100001001011110000100101110000100101100001001110000100110000101100001110000110001100110111",
                length, tg, rab);
        //p.insert(g);
        // 36 5-cliques
        g = new GraphItem("010000100101110101011110101011101001000011110000100101110101011110101011101001000011100001001011101010111101010111010010000110000100101110101011110101011101001000110000100101110101011110101011101001001100001001011101010111101010111010010010000100101110101011110101011101001110000100101110101011110101011101001100001001011101010111101010111010110000100101110101011110101011101110000100101110101011110101011101100001001011101010111101010111110000100101110101011110101011110000100101110101011110101011100001001011101010111101010110000100101110101011110101110000100101110101011110101100001001011101010111101110000100101110101011110110000100101110101011111100001001011101010111110000100101110101011010000100101110101011100001001011101010110000100101110101110000100101110101100001001011101110000100101110110000100101111100001001011110000100101110000100101100001001110000100110000101100001110000110001100110111",
                length, tg, rab);
        p.insert(g);
    }

    /**
     * Fills the population
     * @param population the population to be filled
     * @param popSize the number of things to put in the population
     */
    private void fillPop(Population population, int popSize) {
        while ( population.getCurrentSize() < popSize ) {
            addPopulationMember(population);
        }
    }

    /**
     * Creates new members based on the things already in the population
     * @param iterations The number of times to run
     * @param p To population to run on
     */
    private void iterate(int iterations, Population<GraphItem> p) {
        for ( int i = 0; i < iterations; i++ ) {
            // Iterate; get two random items and combine them
            GraphItem a = p.getRandom(), b = p.getRandom();
            if (a==b) { continue; }
            GraphItem c = GraphItem.createNew(a, b, r);
            int index = p.insert(c);
            if ( showNewBest && index == 0 ) printBest(p);
        }
    }

    /**
     * Prints the current best population member
     * @param population The population to get the member from. 
     */
    private void printBest(Population<GraphItem> population) {
        System.out.println("Rank: " + population.getFirst().getRank());
        System.out.println("Blue connections: "
                + population.getFirst().countZeros() );
        System.out.println("Red connections: "
                + population.getFirst().countOnes() );
        System.out.println("Bitstring:");
        System.out.println(population.getFirst());
    }

}
